using System;
using System.Collections.Generic;
using System.Text;
using MacomberMap.Common.Components;
using MacomberMap.Common.Database_Interface.CIM.Components;
using System.Xml;
using MacomberMap.Common.Types;
using MacomberMap.Common.Database_Interface.CIM;
using MacomberMap.Common.Internals;
using System.IO;
using System.Data.SQLite;

namespace MacomberMap.Loader.Export
{
    /// <summary>
    /// This class holds information on a contingency definition
    /// </summary>
    public class MM_Contingency_Definition : IComparable<MM_Contingency_Definition>
    {
        #region Variable declarations
        /// <summary>The name of the contingency definition</summary>
        public String Name;

        /// <summary>The description of the contingency definition</summary>
        public String Description;

        /// <summary>The rdf:ID of the contingency defintion</summary>
        public MM_rdfID rdfID;

        /// <summary>Our collection of exportable elements</summary>
        public Dictionary<CIM_Element, Guid> ExportableElements = new Dictionary<CIM_Element, Guid>();

        /// <summary>Our collection of all elements</summary>
        public List<CIM_Element> FullElements = new List<CIM_Element>();
        
        /// <summary>The collection of seed elements</summary>
        public List<CIM_Element> Seeds = new List<CIM_Element>(10);

        /// <summary>The collection of groups to which the contingency definition belongs</summary>
        public List<MM_Contingency_Grouping> Groups = new List<MM_Contingency_Grouping>(5);

        /// <summary>Whether the definition was previously defined</summary>
        public bool FromCIM = false;

        /// <summary>The stage of processing in which the definition was defined as radial (if any)</summary>
        public int Radial = -1;

        /// <summary>The stage of processing in which the definition was defined as connected to a unit or transformer (if any)</summary>
        public int UnitOrTransformer = -1;

        /// <summary>Whether the contingency is defined manually.</summary>
        public bool ManualCtg = false;
        #endregion

        #region Initialization
        /// <summary>
        /// Instantiate a new contingency defintion
        /// </summary>
        /// <param name="rdfID"></param>
        /// <param name="Name"></param>
        /// <param name="Description"></param>
        /// <param name="FromCIM"></param>        
        /// <param name="ManualCtg"></param>
        public MM_Contingency_Definition(MM_rdfID rdfID, String Name, String Description, bool FromCIM)
        {
            this.rdfID = rdfID;
            this.Name = Name;
            this.Description = Description;
            this.FromCIM = FromCIM;
            this.ManualCtg = Name.StartsWith("M", StringComparison.CurrentCultureIgnoreCase);
        }


        #endregion

        #region Substation Comparisons
        /// <summary>
        /// This class allows for the comparison of two substations
        /// </summary>
        private class SubstationComparer : IComparer<CIM_Element>
        {
            /// <summary>
            /// Compare two substations
            /// </summary>
            /// <param name="x"></param>
            /// <param name="y"></param>
            /// <returns></returns>
            public int Compare(CIM_Element x, CIM_Element y)
            {
                int[] XFs = new int[] { 0, 0 };
                int[] Units = new int[] { 0, 0 };
                float[] Lines = new float[] { 0f, 0f };




                for (int a = 0; a < 2; a++)
                    foreach (CIM_Element Elem in (a == 0 ? x : y).Elements)
                    {
                        if (Elem.Type == "cim:ACLineSegment")
                        {
                            float MostConservative = float.NaN;
                            foreach (CIM_Element OwnerShareRating in Elem.ParseMultiples("etx:OwnerShareRating"))
                                foreach (CIM_Element StaticRating in OwnerShareRating.ParseMultiples("etx:Rating"))
                                    foreach (CIM_Element NormalRating in StaticRating.ParseMultiples("cim:AnalogLimit"))
                                        if ((StaticRating.Name.Equals("staticRating", StringComparison.CurrentCultureIgnoreCase) || StaticRating.Name.Equals("Static", StringComparison.CurrentCultureIgnoreCase)) && (NormalRating.Name.Equals("normalRating", StringComparison.CurrentCultureIgnoreCase) || NormalRating.Name.Equals("NMRL", StringComparison.CurrentCultureIgnoreCase) || NormalRating.Name.Equals("NRML", StringComparison.CurrentCultureIgnoreCase)) && (float.IsNaN(MostConservative) || MostConservative > float.Parse(NormalRating["cim:AnalogLimit.value"])))
                                            MostConservative = float.Parse(NormalRating["cim:AnalogLimit.value"]);
                            if (float.IsNaN(MostConservative))
                                Console.WriteLine("Whoops!");

                            Lines[a] += MostConservative;
                        }
                        else if (Elem.Type == "cim:SynchronousMachine")
                            Units[a]++;
                        else if (Elem.Type == "cim:TransformerWinding")
                            XFs[a]++;




                    }


                int Comp;
                if ((Comp = Lines[0].CompareTo(Lines[1])) != 0)
                    return Comp;
                else if ((Comp = XFs[0].CompareTo(XFs[1])) != 0)
                    return Comp;
                else if ((Comp = Units[0].CompareTo(Units[1])) != 0)
                    return Comp;
                else
                    return x.Name.CompareTo(y.Name);
            }
        }

        #endregion

        #region Exporting
        /// <summary>
        /// Write out an attribute, choosing the CIM version (M* ctg), or default.        
        /// </summary>
        /// <remarks>Added 12/8/2011 to support M* contingencies</remarks>
        /// <param name="AttributeName"></param>
        /// <param name="DefaultValue"></param>
        /// <param name="xW"></param>
        private void WriteElementString(XmlTextWriter xW, String AttributeName, String DefaultValue)
        {
            xW.WriteElementString(AttributeName, DefaultValue);
        }

        /// <summary>
        /// Write out an attribute, choosing the CIM version (M* ctg), or default.        
        /// </summary>
        /// <remarks>Added 12/8/2011 to support M* contingencies</remarks>
        /// <param name="AttributeName"></param>
        /// <param name="DefaultValue"></param>
        /// <param name="xW"></param>
        private void WriteResourceAttribute(XmlTextWriter xW, String AttributeName, String DefaultValue)
        {
            xW.WriteStartElement(AttributeName);
            xW.WriteAttributeString("rdf:resource", DefaultValue);
            xW.WriteEndElement();                
        }


        /// <summary>
        /// Export the contingency definition 
        /// </summary>
        /// <param name="xW"></param>
        /// <remarks>Modified 12/1/2011 to pull in the activemode attribute</remarks>
        public void WriteDefinition(XmlTextWriter xW)
        {           
            //If we have a manual contingency, export all exportable elements
             if (ManualCtg)
            {
                CIM_Element RealElem = MM_Database_CIM.Elements[this.rdfID];
                xW.WriteStartElement("cim:Contingency");
                xW.WriteAttributeString("rdf:ID", this.rdfID.ToString());
                foreach (KeyValuePair<String, String> kvp in RealElem.Attributes)
                    if (kvp.Key.Contains(":"))
                    {
                        if (kvp.Value.StartsWith("#") || kvp.Value.StartsWith("http://"))
                        {
                            xW.WriteStartElement(kvp.Key);
                            xW.WriteAttributeString("rdf:resource", kvp.Value);
                            xW.WriteEndElement();
                        }
                        else
                            xW.WriteElementString(kvp.Key, kvp.Value);
                    }
                xW.WriteEndElement();
                ExportableElements.Clear();
                foreach (CIM_Element Elem in RealElem.Elements)
                    ExportableElements.Add(Elem, Guid.NewGuid());

            }
            else
            {
               


                //First, write out our contingency definition
                xW.WriteStartElement("cim:Contingency");
                xW.WriteAttributeString("rdf:ID", this.rdfID.ToString());
                WriteElementString(xW, "cim:IdentifiedObject.name", this.Name.ToUpper());
                WriteElementString(xW, "cim:IdentifiedObject.aliasName", "");
                WriteElementString(xW, "cim:IdentifiedObject.description", (this.Description.Length > 28 ? this.Description.Substring(0, 28) : this.Description));
                WriteElementString(xW, "cim:IdentifiedObject.localName", "");
                WriteElementString(xW, "cim:IdentifiedObject.mRID", MM_Exporter_Contingencies.GetMRID().ToString());
                WriteElementString(xW, "cim:IdentifiedObject.pathName", "");
                WriteElementString(xW, "etx:Contingency.ActionType", "");
                CIM_Element DoubleCircuit;
                String ActiveMode="", RunFlag="";

                if (this.Name.StartsWith("D") && MM_Database_CIM.Elements.TryGetValue(this.rdfID, out DoubleCircuit))
                {
                    ActiveMode = DoubleCircuit["etx:Contingency.activeMode"];
                    RunFlag = DoubleCircuit["etx:Contingency.RunFlag"];
                }

                if (String.IsNullOrEmpty(ActiveMode))
                    WriteResourceAttribute(xW, "etx:Contingency.activeMode", "http://www.ercot.com/CIM11R0/2008/2.0/extension#EnumContingencyActiveMode.default");
                else
                    WriteResourceAttribute(xW, "etx:Contingency.activeMode", ActiveMode);

                 if (String.IsNullOrEmpty(RunFlag))
                     WriteElementString(xW, "etx:Contingency.RunFlag", "true");
                 else
                     WriteElementString(xW, "etx:Contingency.RunFlag", RunFlag);
                
                WriteElementString(xW, "etx:Contingency.enableCascading", "false");
                WriteElementString(xW, "etx:Contingency.enableRedefinition", "false");
                WriteElementString(xW, "etx:Contingency.enableSPS", "true");
                WriteElementString(xW, "etx:Contingency.generationLoss", "9999");
                WriteResourceAttribute(xW, "etx:Contingency.generatorParticipationFactor", "http://www.ercot.com/CIM11R0/2008/2.0/extension#EnumGeneratorParticipationFactor.normal");
                WriteElementString(xW, "etx:Contingency.loadLoss", "9999");
                WriteResourceAttribute(xW, "etx:Contingency.processType", "http://www.ercot.com/CIM11R0/2008/2.0/extension#EnumContingencyProcessType.useWide");
                WriteElementString(xW, "etx:Contingency.refactor", "false");
                xW.WriteEndElement();
            }

            //Now, export the linkages to all elements
            foreach (KeyValuePair<CIM_Element, Guid> Elem in this.ExportableElements)
            {
                xW.WriteStartElement("etx:ContingencyElement");
                xW.WriteAttributeString("rdf:ID", "_" + Elem.Value.ToString("B").ToUpper());
                xW.WriteElementString("cim:IdentifiedObject.name", Elem.Key.Name);
                xW.WriteElementString("cim:IdentifiedObject.aliasName", "");
                xW.WriteElementString("cim:IdentifiedObject.description", Elem.Key["cim:IdentifiedObject.description"]);
                xW.WriteElementString("cim:IdentifiedObject.localName", "");
                xW.WriteElementString("cim:IdentifiedObject.mRID", MM_Exporter_Contingencies.GetMRID().ToString());
                xW.WriteElementString("cim:IdentifiedObject.pathName", "");
                xW.WriteElementString("etx:ContingencyElement.status", "true");

                xW.WriteStartElement("etx:ContingencyElement.MemberOf_Contingency");
                xW.WriteAttributeString("rdf:resource", "#" + this.rdfID.ToString());
                xW.WriteEndElement();

                xW.WriteStartElement("etx:ContingencyElement.Equipment");
                xW.WriteAttributeString("rdf:resource", "#" + Elem.Key.rdfID.ToString());
                xW.WriteEndElement();

                xW.WriteEndElement();
            }

        }
        #endregion

        #region IComparable<MM_Contingency_Definition> Members
        /// <summary>
        /// Compare two contingency definitions
        /// </summary>
        /// <param name="Other">The other contingency definition</param>
        /// <returns></returns>
        public int CompareTo(MM_Contingency_Definition Other)
        {
            int ThisIndex = 0, OtherIndex = 0;

            for (int a = 0; a < this.Groups.Count; a++)
                if (a == 0 || this.Groups[a].Index < ThisIndex)
                    ThisIndex = this.Groups[a].Index;

            for (int a = 0; a < Other.Groups.Count; a++)
                if (a == 0 || Other.Groups[a].Index < OtherIndex)
                    OtherIndex = Other.Groups[a].Index;


            return ThisIndex.CompareTo(OtherIndex);
        }
        #endregion

        #region Identification
        /// <summary>
        /// Provide an easier-to-read view
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.Description;
        }
        #endregion

        /// <summary>
        /// Go through the current contingnecy definition, and determine whether it could be classified as radial (1 or fewer non-radial breaker, no unit or transformer)
        /// </summary>
        /// <param name="RadialBreakers"></param>
        /// <param name="CtgAttribute"></param>
        /// <param name="Increment"></param>
        /// <param name="BreakerTally"></param>
        /// <returns></returns>
        public bool CheckRadial(Dictionary<CIM_Element, bool> RadialBreakers, String CtgAttribute, int Increment, Dictionary<CIM_Element, int> BreakerTally)
        {
             List<CIM_Element> Breaker = new List<CIM_Element>();
            foreach (CIM_Element Elem in ExportableElements.Keys)
                if (Elem.Type == "cim:TransformerWinding" || Elem.Type == "cim:PowerTransformer" || Elem.Type == "cim:SynchronousMachine" || Elem.Type == "cim:ShuntCompensator" || Elem.Type == "cim:StaticVarCompensator")
                    return false;
                else if (Elem.Type == "cim:Breaker" && !RadialBreakers.ContainsKey(Elem))
                    if (String.IsNullOrEmpty(CtgAttribute) || XmlConvert.ToBoolean(Elem[CtgAttribute]))
                        if (!BreakerTally.ContainsKey(Elem) || BreakerTally[Elem] > 1)
                            Breaker.Add(Elem);
            if (Breaker.Count < 2)
            {
                foreach (CIM_Element ThisBreaker in Breaker)
                    RadialBreakers.Add(ThisBreaker, true);
                Radial = Increment;
                return true;
            }
            else
                return false;
        }


        /// <summary>   
        /// Generate the description for our element
        /// </summary>
        /// <param name="FoundLine"></param>
        /// <param name="CheckAutos"></param>
        /// <param name="Substations"></param>
        /// <param name="Lines"></param>
        /// <param name="XFs"></param>
        /// <param name="LineLevels"></param>
        /// <param name="XFLevels"></param>
        /// <param name="Units"></param>
        /// <param name="ShuntCompensators"></param>
        public void GenerateDescription(bool FoundLine, bool CheckAutos, List<CIM_Element> Substations, List<CIM_Element> Lines, List<MM_KVLevel> LineLevels, List<MM_KVLevel> XFLevels, List<CIM_Element> Units, List<CIM_Element> XFs, List<CIM_Element> ShuntCompensators)
        {
            //TODO: Modify substation names to LongName
            //TODO: Tally list of cim:Line, and add to FoundLine segment
            //TODO: Modify substation selection for lines, to #1 and #2 rated subs
            if (FromCIM || ManualCtg)
                return;
            else if (FoundLine)
            {
                Substations.Sort(new SubstationComparer());
                LineLevels.Sort();
                StringBuilder KVLevel = new StringBuilder();
                foreach (MM_KVLevel Voltage in LineLevels)
                    KVLevel.Append((KVLevel.Length == 0 ? "" : "/") + Voltage.Name.Split(' ')[0]);
                //String KVLevel = LineLevels[0].Name.Split(' ')[0];
                //String Sub1 = Substations[0].Name.Length > 2 ? Substations[0].Name.Substring(0, 3).ToUpper() : Substations[0].Name.ToUpper();
                //String Sub2 = Substations[Substations.Count - 1].Name.Length > 2 ? Substations[Substations.Count - 1].Name.Substring(0, 3).ToUpper() : Substations[Substations.Count - 1].Name.ToUpper();
                Description = MM_Repository.TitleCase(Substations[0].LongName) + " to " + MM_Repository.TitleCase(Substations[Math.Min(1, Substations.Count - 1)].LongName) + (Lines.Count == 1 ? " " : " (" + Lines.Count.ToString() + ")") + KVLevel.ToString() + " KV";
            }
            else if (CheckAutos)
            {
                //Make sure we have a transformer
                if (XFs.Count == 0)
                    foreach (CIM_Element Elem in this.Seeds)
                        if (Elem.Type == "cim:TransformerWinding" && Elem["cim:TransformerWinding.windingType"].EndsWith("primary", StringComparison.CurrentCultureIgnoreCase))
                            XFs.Add(Elem);
                XFLevels.Sort();
                StringBuilder KVLevel = new StringBuilder();
                foreach (MM_KVLevel Voltage in XFLevels)
                    KVLevel.Append((KVLevel.Length == 0 ? "" : "/") + Voltage.Name.Split(' ')[0]);
                //String Sub1 = Substations[0].Name.Length > 2 ? Substations[0].Name.Substring(0, 3).ToUpper() : Substations[0].Name.ToUpper();
                //String KVLevel1 = XFLevels[0].Name.Split(' ')[0];
                //String KVLevel2 = XFLevels[XFLevels.Count - 1].Name.Split(' ')[0];
                Description = MM_Repository.TitleCase(Substations[0].LongName) + " " + MM_Repository.TitleCase(XFs[0].Name) + (XFs.Count == 1 ? " " : " (" + XFs.Count.ToString() + ")") + KVLevel.ToString() + " KV";
            }
            else if (Units.Count > 0)
            {
                StringBuilder sB = new StringBuilder(MM_Repository.TitleCase(Substations[0].LongName));
                for (int a=0; a<Units.Count; a++)
                    if (a == 0)
                        sB.Append((Units.Count == 1 ? " Unit " : " Units") + MM_Repository.TitleCase(Units[a].Name));
                    else
                        sB.Append("," + MM_Repository.TitleCase(Units[a].Name));
                Description = sB.ToString();
            }
            else if (ShuntCompensators.Count > 0)
            {
                StringBuilder sB = new StringBuilder(MM_Repository.TitleCase(Substations[0].LongName));
                for (int a = 0; a < ShuntCompensators.Count; a++)
                    if (a == 0)
                        sB.Append((ShuntCompensators.Count == 1 ? " Shunt Compensator " : " Shunt Compensators") + MM_Repository.TitleCase(ShuntCompensators[a].Name));
                    else
                        sB.Append("," + MM_Repository.TitleCase(ShuntCompensators[a].Name));
                Description = sB.ToString();
            }
        }

        /// <summary>
        /// Identify this contingency definition based on the list of seed elements
        /// </summary>
        /// <param name="LogFile">The log file</param>
        /// <param name="OutgoingDefinitions">The outgoing definitions</param>
        /// <param name="sConn">The SQLite connector from which the names should be investigated</param>
        /// <param name="Deltas"></param>
        /// <param name="CheckAutos"></param>
        /// <param name="FoundLine"></param>
        /// <param name="LineLevels"></param>
        /// <param name="Substations"></param>
        /// <param name="Units"></param>
        /// <param name="Lines"></param>
        /// <param name="ShuntCompensators"></param>
        /// <param name="XFLevels"></param>
        /// <param name="XFs"></param>
        public void GenerateName(Dictionary<String, MM_Contingency_Definition> OutgoingDefinitions, StreamWriter LogFile, SQLiteConnection sConn, List<String> Deltas, out  bool FoundLine, out bool CheckAutos,  out List<CIM_Element> Substations , out List<CIM_Element> Lines, out List<MM_KVLevel> LineLevels, out List<MM_KVLevel> XFLevels, out List<CIM_Element> Units , out  List<CIM_Element> XFs, out List<CIM_Element> ShuntCompensators)        
        {
            //First, if we're identified in CIM, return
            
            
           
            //Check to see what kind of equipment we have
            FoundLine = false;
            CheckAutos = false;
            foreach (MM_Contingency_Grouping cGrp in this.Groups)
                if (cGrp.Name.EndsWith("AT"))
                    CheckAutos = true;
            Substations = new List<CIM_Element>();
            LineLevels = new List<MM_KVLevel>();
            XFLevels = new List<MM_KVLevel>();
            Units = new List<CIM_Element>();
            XFs = new List<CIM_Element>();
            Lines = new List<CIM_Element>();
            ShuntCompensators = new List<CIM_Element>();
            if (FromCIM || ManualCtg)
                return;

            foreach (CIM_Element Elem in ExportableElements.Keys)
            {
                if (Elem.Type == "cim:ACLineSegment")
                {
                    FoundLine = true;
                    LineLevels.Add(MM_Database_CIM.LocateKVLevel(Elem.VoltageLevel));
                    Lines.Add(Elem);
                    foreach (CIM_Element ACSub in Elem.ACSegmentStations)
                        if (!Substations.Contains(ACSub))
                            Substations.Add(ACSub);
                }
                else if (Elem.Type == "cim:SynchronousMachine")
                    Units.Add(Elem);
                else if (Elem.Type == "cim:TransformerWinding" && CheckAutos)
                {
                    MM_KVLevel KVLevel = MM_Database_CIM.LocateKVLevel(Elem.VoltageLevel);
                    if (KVLevel.Name != "Other KV")
                        XFLevels.Add(KVLevel);
                }
                
                //JLH
                else if (Elem.Type == "cim:ShuntCompensator" || Elem.Type == "cim:StaticVarCompensator")
                    ShuntCompensators.Add(Elem);
                if (Elem.Substation != null)
                    if (!Substations.Contains(Elem.Substation))
                        Substations.Add(Elem.Substation);
            }

            //Now, look at our seed information to determine whether we have a match, if we have seed elements
            if (Seeds.Count > 0)
            {
                StringBuilder sBd = new StringBuilder();
                foreach (CIM_Element SeedElem in Seeds)
                    if (sBd.Length == 0)
                        sBd.Append("SELECT * FROM Contingency_Seeds WHERE TEID=" + MM_Database_CIM.ReverseTEIDs[SeedElem].ToString());
                    else
                        sBd.Append(" OR TEID=" + MM_Database_CIM.ReverseTEIDs[SeedElem].ToString());

                Dictionary<String, List<UInt64>> Titles = new Dictionary<string, List<ulong>>();
                string LastTitle = "";
                using (SQLiteCommand sCmd = new SQLiteCommand(sBd.ToString(), sConn))
                using (SQLiteDataReader sRd = sCmd.ExecuteReader())
                    while (sRd.Read())
                    {
                        String ThisRow = (string)sRd["Name"] + "." + (sRd["rdfID"] is String ? (string)sRd["rdfID"] : "");
                        if (!Titles.ContainsKey(ThisRow))
                            Titles.Add(ThisRow, new List<ulong>());
                        Titles[ThisRow].Add(Convert.ToUInt64(sRd["TEID"]));
                        LastTitle = ThisRow;
                    }              
                



                //Now, check the number of matches, and make sure everything matches up. If it doesn't, we'll need to create a log entry indicating a change.     
                if (Titles.Count == 1 && Seeds.Count > 0)
                    foreach (KeyValuePair<string, List<UInt64>> kvp in Titles)
                    {
                        //First, make sure every one matches
                        List<UInt64> OutTEIDs = new List<ulong>(Seeds.Count);
                        foreach (CIM_Element Elem in Seeds)
                            OutTEIDs.Add(MM_Database_CIM.ReverseTEIDs[Elem]);
                        OutTEIDs.Sort();
                        kvp.Value.Sort();

                        bool AllMatched = true;
                        foreach (UInt64 TestTEID in kvp.Value)
                            if (AllMatched && !OutTEIDs.Contains(TestTEID))
                                AllMatched = false;
                        foreach (UInt64 TestTEID in OutTEIDs)
                            if (AllMatched && !kvp.Value.Contains(TestTEID))
                                AllMatched = false;

                        //Now, load in the full definition for this element, to see what we need to handle
                        using (SQLiteCommand sCmd = new SQLiteCommand("SELECT TEID FROM Contingency_Seeds WHERE NAME=:0", sConn))
                        {
                            sCmd.Prepare();
                            sCmd.Parameters.AddWithValue(":0", kvp.Key.Split('.')[0]);
                            kvp.Value.Clear();
                            using (SQLiteDataReader sRd = sCmd.ExecuteReader())
                                while (sRd.Read())
                                    kvp.Value.Add(Convert.ToUInt64(sRd["TEID"]));
                        }

                        //Run through, and update our delta logs with our differences
                        int Adds = 0, Removes = 0;
                        foreach (CIM_Element Elem in this.Seeds)
                            if (!kvp.Value.Remove(MM_Database_CIM.ReverseTEIDs[Elem]))
                            {
                                Deltas.Add(string.Format("Contingency {0} added {1} {2} (rdfID {3} / TEID {4})", kvp.Key.Split('.')[0], Elem.Type.Split(':')[1], Elem.Name, Elem.rdfID, Elem.TEID));
                                Adds++;
                            }
                        foreach (UInt64 TEIDRemoved in kvp.Value)
                        {
                            Deltas.Add(string.Format("Contingency {0} removed TEID {1}", kvp.Key.Split('.')[0], TEIDRemoved));
                            Removes++;
                        }

                        if (kvp.Key.EndsWith("."))
                            this.rdfID = new MM_rdfID("_" + Guid.NewGuid().ToString("B").ToUpper());
                        else
                            this.rdfID = new MM_rdfID(kvp.Key.Split('.')[1]);

                        //Make sure our name fits what we're looking for (handle definitions w/ seed elements losing them, etc.)
                        string ProposedName = kvp.Key.Split('.')[0];
                      
                        if ((ProposedName.StartsWith("N_") && !FoundLine && Units.Count == 0 && !CheckAutos && ShuntCompensators.Count == 0)
                            || (ProposedName.StartsWith("S") && FoundLine)
                            || (ProposedName.StartsWith("U") && Units.Count > 0)
                            || (ProposedName.StartsWith("X") && CheckAutos) 
                            || (ProposedName.StartsWith("Q") && ShuntCompensators.Count > 0))
                        {
                            this.Name = ProposedName;
                            RemoveName(this.Name, sConn);
                            AddNewName(sConn);
                            if (Adds == 0 && Removes == 0)
                                Console.WriteLine("{0:#,##0}: Restored name for {1}", OutgoingDefinitions.Count+1, this.Name);
                            else
                                Console.WriteLine("{0:#,##0}: Had updates for {1}: {2:#,##0} additions, {3:#,##0} removals, {4} remaining.",OutgoingDefinitions.Count+1,  this.Name, Adds, Removes, Math.Abs(this.Seeds.Count - Removes));
                            return;
                        }
                    }
                else if (Titles.Count > 0)
                {
                    //If we have two or more titles, that means we have a split definition. Take all definitions, and lock them so we can't use their names again.
                    StringBuilder sB = new StringBuilder();
                    foreach (String str in Titles.Keys)
                    {
                        if (sB.Length == 0)
                            sB.AppendFormat("Contingency {0} has subsumed the following definitions, which have been removed: {1}", this.Description, str.Split('.')[0]);
                        else
                            sB.AppendFormat(", {0}", str.Split('.')[0]);
                        RemoveName(str.Split('.')[0],sConn);
                        using (SQLiteCommand sCmd = new SQLiteCommand("INSERT OR REPLACE INTO Reserved_Contingency_Names ([Name]) VALUES (:Name)", sConn))
                        {
                            sCmd.Prepare();
                            sCmd.Parameters.AddWithValue("Name", str.Split('.')[0]);
                            try
                            {
                                sCmd.ExecuteNonQuery();
                            }
                            catch (Exception e)
                            {
                                Console.WriteLine(e);

                            }

                        }
                    }
                    Deltas.Add(sB.ToString());
                }
            
            }

           

            //Now, if we have found a line, let's look at the substations
            if (FoundLine)
            {
                Substations.Sort(new SubstationComparer());
                LineLevels.Sort();

                //Determine the pieces needed for our name
                String KVLevel = LineLevels[0].Name.Split(' ')[0];
                String Sub1 = Substations[0].Name.Length > 2 ? Substations[0].Name.Substring(0, 3).ToUpper() : Substations[0].Name.ToUpper();
                String Sub2 = Substations[Substations.Count - 1].Name.Length > 2 ? Substations[Substations.Count - 1].Name.Substring(0, 3).ToUpper() : Substations[Substations.Count - 1].Name.ToUpper();
                String TempName = (MM_Repository.TitleCase("S" + Sub1 + Sub2 + KVLevel.Substring(KVLevel.Length - 1, 1))).ToUpper();


                if (Substations.Count > 2)
                {
                    LogFile.Write("{0}: Multiple lines / > 2 substations detected: ", TempName);
                    for (int a = 0; a < Substations.Count; a++)
                        if (a == Substations.Count - 1)
                            LogFile.WriteLine(Substations[a].Name);
                        else
                            LogFile.Write(Substations[a].Name + ", ");
                }


                //If we have a name duplication, handle that                                                                           
                if (CheckUniqueName(TempName, sConn, OutgoingDefinitions))
                {
                    Deltas.Add(String.Format("Contingency {0} / {1} ({2}) was added.", TempName, this.Name, this.Description));
                    this.Name = TempName;
                    
                }
                else
                {
                    this.Name = FindNewName(TempName, Sub1.Length + Sub2.Length, OutgoingDefinitions, LogFile, sConn, Deltas).ToUpper();
                    LogFile.WriteLine("{0}: Duplicate name encountered: renamed to {1}", TempName, this.Name);
                    Deltas.Add(String.Format("Contingency {0}'s name was a duplicate, and renamed to {1}", TempName, this.Name));
                }
                AddNewName(sConn);
            }
            else if (CheckAutos)
            {
                XFLevels.Sort();
                String Sub1 = Substations[0].Name.Length > 2 ? Substations[0].Name.Substring(0, 3).ToUpper() : Substations[0].Name.ToUpper();
                String KVLevel1 = XFLevels[0].Name.Split(' ')[0];
                String KVLevel2 = XFLevels[XFLevels.Count - 1].Name.Split(' ')[0];
                String TempName = (MM_Repository.TitleCase("X" + Sub1 + KVLevel1.Substring(KVLevel1.Length - 1) + KVLevel2.Substring(KVLevel2.Length - 1))).ToUpper();

                


                foreach (CIM_Element XFW in ExportableElements.Keys)
                    if (XFW.Type == "cim:TransformerWinding" && !XFs.Contains(XFW.Parse("cim:PowerTransformer")))
                        XFs.Add(XFW.Parse("cim:PowerTransformer"));


                if (CheckUniqueName(TempName, sConn, OutgoingDefinitions))
                {
                    this.Name = TempName;
                    Deltas.Add(String.Format("Contingency {0}", this.Name));
                }
                else
                {
                    this.Name = FindNewName(TempName, Sub1.Length+1, OutgoingDefinitions, LogFile, sConn, Deltas).ToUpper();
                    LogFile.WriteLine("{0}: Duplicate name encountered: renamed to {1}", TempName, this.Name);
                    Deltas.Add(String.Format("Contingency {0}'s name was a duplicate, and renamed to {1}", TempName, this.Name));
                }

                if (XFs.Count > 1)
                {
                    LogFile.Write("{0}: Multiple transformers detected: ", TempName);
                    for (int a = 0; a < XFs.Count; a++)
                        if (a == XFs.Count - 1)
                            LogFile.WriteLine(XFs[a].Name);
                        else
                            LogFile.Write(XFs[a].Name + ", ");
                }
                AddNewName(sConn);
            }
            else if (Units.Count > 0)
            {
                String Sub1 = Substations[0].Name.Length > 2 ? Substations[0].Name.Substring(0, 3).ToUpper() : Substations[0].Name.ToUpper();
                String Unit1 = Units[0].Name.Length > 2 ? Units[0].Name.Substring(0, 3).ToUpper() : Units[0].Name.ToUpper();
                String TempName = (MM_Repository.TitleCase("U" + Sub1 + Unit1 + Units.Count.ToString())).ToUpper();
                if (CheckUniqueName(TempName, sConn, OutgoingDefinitions))
                {
                    Deltas.Add(String.Format("Contingency {0} / {1} (2) was added.", TempName, this.Name, this.Description));
                    this.Name = TempName;
                }
                else
                {
                    this.Name = FindNewName(TempName, Sub1.Length, OutgoingDefinitions, LogFile, sConn, Deltas).ToUpper();
                    if (this.Name == TempName)
                        Console.WriteLine("Whoops!");
                    Console.WriteLine("{0}: Duplicate name encountered: renamed to {1}", TempName, this.Name); 
                    
                    LogFile.WriteLine("{0}: Duplicate name encountered: renamed to {1}", TempName, this.Name); 
                    Deltas.Add(String.Format("Contingency {0}'s name was a duplicate, and renamed to {1}", TempName, this.Name));
                }

                AddNewName(sConn);
            }
            else if (ShuntCompensators.Count > 0)
            {
                String KVLevel = ShuntCompensators[0].VoltageLevel.Name.Split(' ')[0];                
                String Sub1 = Substations[0].Name.Length > 2 ? Substations[0].Name.Substring(0, 3).ToUpper() : Substations[0].Name.ToUpper();
                String SC = ShuntCompensators[0].Name.Length > 2 ? ShuntCompensators[0].Name.Substring(0, 3).ToUpper() : ShuntCompensators[0].Name.ToUpper();
                String TempName = (MM_Repository.TitleCase("Q" + Sub1 + SC +  KVLevel.Substring(KVLevel.Length - 1, 1))).ToUpper();
                if (CheckUniqueName(TempName, sConn, OutgoingDefinitions))
                {
                    Deltas.Add(String.Format("Contingency {0} / {1} (2) was added.", TempName, this.Name, this.Description));
                    this.Name = TempName;
                }
                else
                {
                    this.Name = FindNewName(TempName, Sub1.Length, OutgoingDefinitions, LogFile, sConn, Deltas).ToUpper();
                    if (this.Name == TempName)
                        Console.WriteLine("Whoops!");
                    Console.WriteLine("{0}: Duplicate name encountered: renamed to {1}", TempName, this.Name);

                    LogFile.WriteLine("{0}: Duplicate name encountered: renamed to {1}", TempName, this.Name);
                    Deltas.Add(String.Format("Contingency {0}'s name was a duplicate, and renamed to {1}", TempName, this.Name));
                }

                AddNewName(sConn);
            }
            else
            {
                //Determine if we have a transformer, and let the operator know.
                bool FoundXF = false;
                CIM_Element FirstElem = null;
                foreach (CIM_Element Elem in ExportableElements.Keys)
                {
                    if (FirstElem == null)
                        FirstElem = Elem;
                    if (Elem.Type == "cim:TransformerWinding")
                    {
                        FoundXF = true;
                        CIM_Element XF = Elem.Parse("cim:PowerTransformer");
                        this.Description = MM_Repository.TitleCase(Elem.Substation.Name) + " " + MM_Repository.TitleCase(XF.Name) + " " + XF.TransformerWinding[0].VoltageLevel.Name.Split(' ')[0] + "/" + XF.TransformerWinding[1].VoltageLevel.Name.Split(' ')[0] + "KV";
                    }
                }
                if (FoundXF)
                    LogFile.WriteLine("{0}: Transformer windings detected, but not assigned to auto-transformer groupings", this.Name);
                else if (Seeds.Count > 0)
                    Description = MM_Repository.TitleCase(Seeds[0].Substation.Name) + " " + Seeds[0].Type.Split(':')[1] + " " + MM_Repository.TitleCase(Seeds[0].Name);
                else if (ExportableElements.Count > 0)
                    Description = MM_Repository.TitleCase(FirstElem.Substation.Name) + " " + FirstElem.Type.Split(':')[1] + " " + MM_Repository.TitleCase(FirstElem.Name);
                else
                    LogFile.WriteLine("{0} Unable to find seed or exportable elements for {1}", Name, Description);
                Console.WriteLine("No name for " + this.Name);
                AddNewName(sConn);
            }
            
        }

        /// <summary>
        /// Add a new contingency definition to our list
        /// </summary>
        /// <param name="sConn">The SQLite connection</param>        
        private void AddNewName(SQLiteConnection sConn)
        {
            this.Name = MM_Repository.TitleCase(this.Name);
            
            foreach (CIM_Element Seed in Seeds)
                using (SQLiteCommand sCmd = new SQLiteCommand("INSERT OR REPLACE INTO Contingency_Seeds ([Name],[rdfID],[TEID]) VALUES (:Name, :rdfID, :TEID)", sConn))
                {
                    sCmd.Prepare();
                    sCmd.Parameters.AddWithValue("Name", this.Name);
                    sCmd.Parameters.AddWithValue("rdfID", this.rdfID.ToString());
                    sCmd.Parameters.AddWithValue("TEID", MM_Database_CIM.ReverseTEIDs[Seed]);
                    sCmd.ExecuteNonQuery();
                }

        }

        /// <summary>
        /// Remove a contingency definition's name
        /// </summary>
        /// <param name="CtgName">The contingency name</param>
        /// <param name="sConn">The SQLite connector</param>
        private void RemoveName(String CtgName, SQLiteConnection sConn)
        {
            using (SQLiteCommand sCmd = new SQLiteCommand("DELETE FROM Contingency_Seeds WHERE UPPER([Name])=:Name", sConn))
            {
                sCmd.Prepare();
                sCmd.Parameters.AddWithValue("Name", CtgName.ToUpper());
                sCmd.ExecuteNonQuery();
            }
        }

        /// <summary>
        /// Locate a unique name for a collisioned contingnecy definition name
        /// </summary>
        /// <param name="Name">The target name of the contingency definition</param>
        /// <param name="UpdatePosition">The character position in which the name will be handeled</param>
        /// <param name="OutgoingDefinitions">The collection of contingency defintions </param>
        /// <param name="LogFile">The file for handling logs</param>
        /// <param name="LogLines">The collection of log lines</param>
        /// <param name="sConn"></param>
        /// <returns></returns>
        private string FindNewName(string Name, int UpdatePosition, Dictionary<string, MM_Contingency_Definition> OutgoingDefinitions, StreamWriter LogFile, SQLiteConnection sConn, List<String> LogLines)
        {
            //First, make sure the original definition is updated to reflect its proper name
            using (SQLiteCommand sCmd2 = new SQLiteCommand("SELECT COUNT([Name]) FROM Reserved_Contingency_Names WHERE [Name]=:Name", sConn))
            {
                sCmd2.Prepare();
                sCmd2.Parameters.AddWithValue("Name",Name);
                if (Convert.ToInt32(sCmd2.ExecuteScalar()) == 0 && OutgoingDefinitions.ContainsKey(Name))
                {
                    //Determine the new name for the original, and retrieve the definition
                    String OrigName = Name.Substring(0, UpdatePosition) + "1" + Name.Substring(UpdatePosition + (Name.Length == 8 ? 1 : 0));
                    MM_Contingency_Definition OrigDef = OutgoingDefinitions[Name];

                    //Remove the original definition from dictionaries, and handle names
                    OutgoingDefinitions.Remove(Name);
                    using (SQLiteCommand sCmd = new SQLiteCommand("INSERT INTO Reserved_Contingency_Names ([Name]) VALUES (:Name)", sConn))
                    {
                        sCmd.Prepare();
                        sCmd.Parameters.AddWithValue("Name", Name);
                        sCmd.ExecuteNonQuery();
                    }
                    RemoveName(Name, sConn);

                    //Now, rename our original and update.
                    OrigDef.Name = OrigName;
                    OutgoingDefinitions.Remove(OrigName);
                    OutgoingDefinitions.Add(OrigName, OrigDef);
                    OrigDef.AddNewName(sConn);
                }
            }

            //Now, update our current name, and return it;
            foreach (Char TestChr in "234567890_ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray())
            {
                String NewName = Name.Substring(0, UpdatePosition) + TestChr.ToString() + Name.Substring(UpdatePosition + (Name.Length == 8 ? 1 : 0));
                if (CheckUniqueName(NewName, sConn, OutgoingDefinitions))
                    return NewName;
            }
            LogFile.WriteLine("Unable to locate available duplication name for " + Name);
            return Name;
        }

        /// <summary>
        /// Determine whether a name is contained within the unique table
        /// </summary>
        /// <param name="Name"></param>
        /// <param name="sConn"></param>
        /// <param name="OutgoingDefinitions"></param>
        /// <returns></returns>        
        public bool CheckUniqueName(String Name, SQLiteConnection sConn, Dictionary<String, MM_Contingency_Definition> OutgoingDefinitions)
        {
            int Tally = 0;
            using (SQLiteCommand sCmd = new SQLiteCommand("SELECT COUNT([Name]) FROM Reserved_Contingency_Names WHERE [Name] = :Name", sConn))
            {
                sCmd.Prepare();
                sCmd.Parameters.AddWithValue("Name", MM_Repository.TitleCase(Name));
                Tally +=  Convert.ToInt32(sCmd.ExecuteScalar());
            }
            using (SQLiteCommand sCmd = new SQLiteCommand("SELECT COUNT([Name]) FROM Contingency_Seeds WHERE [Name] = :Name", sConn))
            {
                sCmd.Prepare();
                sCmd.Parameters.AddWithValue("Name", MM_Repository.TitleCase(Name));
                Tally += Convert.ToInt32(sCmd.ExecuteScalar());
            }
            if (OutgoingDefinitions.ContainsKey(MM_Repository.TitleCase(Name)))
                Tally++;
            return Tally == 0;
        }
    }
}
